/*MyPhotoIndex Free Photo Organizer <www.myphotoindex.com>
  Copyright (C) <2006-2009>  <Asaf Yarkoni asaf@yarkoni.net>

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using MyPhotoIndex.Utilities;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using MyPhotoIndex.Utilities.Types;
using System.Collections;
using System.Runtime.InteropServices;
using MyPhotoIndex.Interfaces;
using System.Globalization;
using MyPhotoIndex.Interfaces.Tagging;

namespace MyPhotoIndex.Controls
{
    partial class MyPhotoIndexImageTagTreeView : System.Windows.Forms.TreeView
    {
        public event EventHandler<ScrollValuesEventArgs> ScrollUpdate;

        private static Image m_collapseClosedImage = Resources.Resources.Collapse_Closed;
        private static Image m_collapseCollapsedImage = Resources.Resources.Collapse_Collapsed;

        private static Image m_checkBoxChecked = Resources.Resources.CheckBox_Checked;
        private static Image m_checkBoxUnChecked = Resources.Resources.CheckBox_UnChecked;

        private static Image m_dragDropImage = Resources.Resources.Tag_29;

        private Font m_font = new Font("family", 8, FontStyle.Regular);

        private Pen m_parentBorderPen1;
        private Pen m_parentBorderPen2;
        private Pen m_childBorderPen;
        private Brush m_textBrush;
        private ImageList m_imageListDrag = new ImageList();
        private Rectangle m_dragBoxFromMouseDown;
        private bool m_autoCheck;
        private List<TreeNode> m_visibleNodesCollection;
        private bool m_checkboxes;
        private bool m_icons;
        private List<TreeNode> m_selectedNodes = new List<TreeNode>();

        private MyPhotoIndex.Controls.TreeView.DisplayTypeEnum m_displayType;
        private List<TreeNode> m_filteredNodes = new List<TreeNode>();

        public MyPhotoIndexImageTagTreeView()
        {
            InitializeComponent();

            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.UserPaint, true);
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

            base.CheckBoxes = false;
            base.RightToLeft = RightToLeft.No;
            base.AllowDrop = true;
            base.TreeViewNodeSorter = new TagTreeSorter();
            base.Scrollable = true;
            m_visibleNodesCollection = new List<TreeNode>();

            InitSkin();

            this.MouseClick += new MouseEventHandler(MyPhotoIndexImageTagTreeView_MouseClick);
        }

        void MyPhotoIndexImageTagTreeView_MouseClick(object sender, MouseEventArgs e)
        {
            TreeNode clickedNode = this.GetNodeAt(e.Location);
            if (clickedNode == null)
            {
                return;
            }

            Rectangle collapseRect = (GetCollapseRect(clickedNode));
            Rectangle checkBoxRect = (GetCheckBoxRect(clickedNode));
            if (collapseRect.Contains(e.Location) == true ||
                checkBoxRect.Contains(e.Location) == true)
            {
                return;
            }

            bool isSelected = m_selectedNodes.Contains(clickedNode);

            if ((Control.ModifierKeys & Keys.Control) != Keys.Control &&
                  (Control.ModifierKeys & Keys.Shift) != Keys.Shift &&
                (e.Button != MouseButtons.Right || isSelected == false))
            {
                m_selectedNodes.Clear();
            }

            if (isSelected == true &&
                (Control.ModifierKeys & Keys.Control) == Keys.Control)
            {
                m_selectedNodes.Remove(clickedNode);
            }
            else
                if (isSelected == false)
                {
                    m_selectedNodes.Add(clickedNode);
                }

            InvalidateMe();
        }

        private void InvalidateMe()
        {
            if (m_displayType != TreeView.DisplayTypeEnum.Classic)
            {
                this.SetStyle(ControlStyles.UserPaint, true);
                this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
                this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            }

            this.Invalidate();
        }

        private void InitSkin()
        {
            Color parentBorderColor = Color.FromArgb(79, 94, 113);
            m_parentBorderPen1 = new Pen(parentBorderColor);

            Color parentBorderColor2 = Color.FromArgb(107, 131, 157);
            m_parentBorderPen2 = new Pen(parentBorderColor2);

            Color childBorderColor = Color.FromArgb(227, 231, 234);
            m_childBorderPen = new Pen(childBorderColor);

            Color textColor = Color.FromArgb(79, 95, 111);
            m_textBrush = new SolidBrush(textColor);
        }

        public List<TreeNode> SelectedNodes
        {
            get { return m_selectedNodes; }
            //         set { m_selectedNodes = value; }
        }

        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            switch (keyData)
            {
                case Keys.Space:
                    TreeNode tn = this.SelectedNode;
                    if (tn != null)
                    {
                        tn.Checked = !tn.Checked;
                    }
                    break;
            }

            return base.ProcessCmdKey(ref msg, keyData);
        }

        public void Load(IImageTagCollection tagFactory)
        {
            InvalidateMe();
            try
            {
                this.BeginUpdate();
                base.Nodes.Clear();
                List<IImageTag> tagList = tagFactory.Tags;
                foreach (IImageTag tag in tagList)
                {
                    TreeNode parentNode = Nodes.Add(tag.Id.ToString(CultureInfo.InvariantCulture), tag.Name);
                    parentNode.Tag = tag;

                    Buildtree(parentNode, tag);
                }

                this.Sort();
            }
            catch (Exception ex)
            {
                Logger.Log(ex);
            }

            this.EndUpdate();

            UpdateTreeNodesHeight();
        }

        public void UpdateTreeNodesHeight()
        {
            if (OSUtils.GetOSInfo().StartsWith("Windows 7") == true)
            {
                this.ItemHeight = 30;
                return;// widows 7 handles the node height unexpectedly flickers slot, I mean a-lot)
            }

            this.ItemHeight = 10;
            this.BeginUpdate();
            UpdateTreeNodesHeight(this.Nodes, 3);
            this.EndUpdate();
        }

        private void UpdateTreeNodesHeight(TreeNodeCollection nodes, int factor)
        {
            foreach (TreeNode node in nodes)
            {
                Win32Interop.SetTreeViewItemHeight(this, node, factor);

                UpdateTreeNodesHeight(node.Nodes, 2);
            }
        }

        internal void Buildtree(TreeNode parentNode, IImageTag tag)
        {
            foreach (IImageTag childTag in tag.ChildTags)
            {
                TreeNode childNode = parentNode.Nodes.Add(childTag.Id.ToString(CultureInfo.InvariantCulture), childTag.Name);

                childNode.Tag = childTag;
                Buildtree(childNode, childTag);
            }
        }

        public MyPhotoIndex.Controls.TreeView.DisplayTypeEnum DisplayType
        {
            get { return m_displayType; }
            set
            {
                m_displayType = value;
                if (m_displayType == TreeView.DisplayTypeEnum.Classic)
                {
                    this.SetStyle(ControlStyles.AllPaintingInWmPaint, false);
                    this.SetStyle(ControlStyles.UserPaint, false);
                    this.BackColor = Color.White;
                    //    this.DrawMode = TreeViewDrawMode.OwnerDrawText;
                    this.FullRowSelect = true;
                    this.HideSelection = false;
                }
            }
        }

        public new bool CheckBoxes
        {
            get { return m_checkboxes; }
            set { m_checkboxes = value; }
        }

        public bool Icons
        {
            get { return m_icons; }
            set { m_icons = value; }
        }

        public IImageTag GetTag(int id)
        {
            return GetTag(id, Nodes);
        }

        private IImageTag GetTag(int id, TreeNodeCollection nodes)
        {
            IImageTag tag = null;
            foreach (TreeNode node in nodes)
            {
                tag = (IImageTag)node.Tag;
                if (tag.Id == id)
                {
                    return tag;
                }
                else
                {
                    tag = GetTag(id, node.Nodes);
                    if (tag != null)
                    {
                        break;
                    }
                }
            }

            return tag;
        }

        public TreeNode[] GetCheckedNodes()
        {
            return GetCheckedNodes(Nodes);
        }

        public IEnumerable<TreeNode> FilteredNodes
        {
            get
            {
                foreach (TreeNode node in m_filteredNodes)
                {
                    yield return node;
                }
            }
        }

        internal TreeNode[] GetCheckedNodes(TreeNodeCollection nodes)
        {
            List<TreeNode> checkedNodes = new List<TreeNode>();
            foreach (TreeNode node in nodes)
            {
                if (node.Checked == true)
                {
                    checkedNodes.Add(node);
                }

                TreeNode[] childNodes = GetCheckedNodes(node.Nodes);
                checkedNodes.AddRange(childNodes);
            }

            TreeNode[] returnNodes = new TreeNode[checkedNodes.Count];
            checkedNodes.CopyTo(returnNodes);
            return returnNodes;
        }

        internal static Rectangle GetCheckBoxRect(TreeNode node)
        {
            if (false == ((MyPhotoIndexImageTagTreeView)node.TreeView).CheckBoxes)
            {
                return Rectangle.Empty;
            }

            Rectangle checkBoxRect = new Rectangle();
            Rectangle nodeBounds = node.Bounds;

            int nodeOffset = (node.Level * 15);//15 is the offset of the default tree nodes.
            checkBoxRect.X = nodeBounds.X + ((node.Level > 0) ? (10 + (10 * node.Level)) : 5);// -nodeOffset;
            checkBoxRect.Y = nodeBounds.Y + ((nodeBounds.Height - m_checkBoxChecked.Height) / 2);
            checkBoxRect.Width = m_checkBoxChecked.Width;
            checkBoxRect.Height = m_checkBoxChecked.Height;

            return checkBoxRect;
        }

        internal Rectangle GetTagRect(TreeNode node)
        {
            Rectangle collapseRect = GetCollapseRect(node);
            Point tagPoint = Point.Empty;

            int size = 16;
            if (m_icons == false)
            {
                tagPoint = new Point((int)(collapseRect.Right), (int)((node.Bounds.Y + ((node.Bounds.Height - /*Resources.Resources.clip_16.Height*/size) / 2))));
            }
            else
            {
                tagPoint = new Point((int)(collapseRect.Right + 5) + (node.Level * 15), (int)((node.Bounds.Y + ((node.Bounds.Height - /*Resources.Resources.clip_16.Height*/size) / 2))));
            }

            return new Rectangle(tagPoint, new Size(ItemHeight - 2, ItemHeight - 2));
        }

        internal static Rectangle GetCollapseRect(TreeNode node)
        {
            Rectangle checkBoxRect = GetCheckBoxRect(node);
            //if (node.Nodes.Count == 0)
            //{
            //    return checkBoxRect;
            //}

            Rectangle nodeBounds = node.Bounds;

            Rectangle collapseRect = new Rectangle();
            collapseRect.X = checkBoxRect.Right + 5;// +(node.Level * 15);
            collapseRect.Y = nodeBounds.Y + ((nodeBounds.Height - m_collapseClosedImage.Height) / 2);
            collapseRect.Width = m_collapseClosedImage.Width;
            collapseRect.Height = m_collapseClosedImage.Height;

            return collapseRect;
        }

        protected override void OnAfterCheck(TreeViewEventArgs e)
        {
            if (m_autoCheck == false)
            {
                m_autoCheck = true;
                CheckAllChildNodes(e.Node, e.Node.Checked, (Control.ModifierKeys == Keys.Control));
                base.OnAfterCheck(e);
                m_autoCheck = false;
            }
        }

        internal void CheckAllChildNodes(TreeNode node, bool check, bool filter)
        {
            foreach (TreeNode childNode in node.Nodes)
            {
                CheckAllChildNodes(childNode, check, filter);

                m_filteredNodes.Remove(childNode);

                if (filter)
                {
                    m_filteredNodes.Add(childNode);
                    Rectangle cehckBoxRect = GetCheckBoxRect(node);
                    cehckBoxRect.Inflate(1, 1);
                    this.Invalidate(cehckBoxRect);
                }
                childNode.Checked = check;
            }

            m_filteredNodes.Remove(node);

            if (filter)
            {
                m_filteredNodes.Add(node);
                Rectangle cehckBoxRect = GetCheckBoxRect(node);
                cehckBoxRect.Inflate(1, 1);
                this.Invalidate(cehckBoxRect);
            }

            node.Checked = check;
        }

        public void UncheckAll()
        {
            this.BeginUpdate();
            m_autoCheck = true;
            foreach (TreeNode childNode in this.Nodes)
            {
                CheckAllChildNodes(childNode, false, (Control.ModifierKeys == Keys.Control));
            }
            m_autoCheck = false;
            this.EndUpdate();
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
            {
                if (m_dragBoxFromMouseDown != Rectangle.Empty &&
                    !m_dragBoxFromMouseDown.Contains(e.X, e.Y))
                {
                    TreeNode selectedNode = base.GetNodeAt(e.Location);
                    if (selectedNode == null)
                    {
                        base.OnMouseMove(e);
                        return;
                    }

                    DragDropNode(selectedNode);
                }
            }

            base.OnMouseMove(e);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            TreeNode selectedNode = base.GetNodeAt(e.Location);
            bool isDragable = (selectedNode != null);

            if (isDragable == true)
            {
                Size dragSize = SystemInformation.DragSize;
                m_dragBoxFromMouseDown = new Rectangle(new Point(e.X - (dragSize.Width / 2),
                                                               e.Y - (dragSize.Height / 2)), dragSize);
            }
            else
            {
                m_dragBoxFromMouseDown = Rectangle.Empty;
            }

            base.OnMouseDown(e);
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            m_dragBoxFromMouseDown = Rectangle.Empty;

            base.OnMouseUp(e);
        }

        protected override void OnNodeMouseClick(TreeNodeMouseClickEventArgs e)
        {
            TreeNode selectedNode = e.Node;

            Win32Interop.ShowScrollBar(this.Handle, Win32Interop.SB_VERT, 0);
            Win32Interop.ShowScrollBar(this.Handle, Win32Interop.SB_HORZ, 0);

            Rectangle checkBoxRect = GetCheckBoxRect(selectedNode);
            bool onCheckBox = checkBoxRect.Contains(e.Location) ? true : false;

            Rectangle collapseRect = GetCollapseRect(selectedNode);
            bool onCollapse = collapseRect.Contains(e.Location) ? true : false;

            if (e.Button == MouseButtons.Left &&
                onCollapse == true)
            {
                this.BeginUpdate();
                if (selectedNode.IsExpanded == false)
                {
                    selectedNode.Expand();
                }
                else
                {
                    selectedNode.Collapse(true);
                }
                this.EndUpdate();
                return;
            }

            if (onCheckBox == true)
            {
                m_filteredNodes.Remove(selectedNode);

                if (Control.ModifierKeys == Keys.Control)
                {
                    if (selectedNode.Checked == false)
                    {
                        m_filteredNodes.Add(selectedNode);
                    }
                    else
                    {
                        if (m_filteredNodes.Contains(selectedNode) == false)
                        {
                            m_filteredNodes.Add(selectedNode);
                            selectedNode.Checked = true;
                            Rectangle cehckBoxRect = GetCheckBoxRect(selectedNode);
                            cehckBoxRect.Inflate(1, 1);
                            this.Invalidate(cehckBoxRect);
                            return;
                        }
                    }
                }

                selectedNode.Checked = !selectedNode.Checked;
            }

            selectedNode.TreeView.SelectedNode = selectedNode;

            base.OnNodeMouseClick(e);
        }

        protected override void OnAfterSelect(TreeViewEventArgs e)
        {
            e.Node.EnsureVisible();
            this.Invalidate();
            base.OnAfterSelect(e);
        }

        protected override void OnResize(EventArgs e)
        {
            UpdateScrollBar();
            base.OnResize(e);
        }

        protected override void OnAfterCollapse(TreeViewEventArgs e)
        {
            UpdateScrollBar();
            base.OnAfterCollapse(e);
        }

        protected override void OnAfterExpand(TreeViewEventArgs e)
        {
            UpdateScrollBar();
            base.OnAfterExpand(e);
        }

        private void UpdateScrollBar()
        {
            m_visibleNodesCollection.Clear();

            this.BeginUpdate();

            Win32Interop.ShowScrollBar(this.Handle, Win32Interop.SB_VERT, 0);
            Win32Interop.ShowScrollBar(this.Handle, Win32Interop.SB_HORZ, 0);

            Win32Interop.SCROLLINFO scrollInfo = new Win32Interop.SCROLLINFO();
            scrollInfo.fMask = Win32Interop.SIF_ALL;
            scrollInfo.cbSize = 28;
            int res = Win32Interop.GetScrollInfo(this.Handle, Win32Interop.SB_VERT, ref scrollInfo);

            int totalHeight = 0;
            int visibleCount = GetVisibleNodesCount(Nodes, ref totalHeight);

            int numOfExpandedNodes = GetExpandedNodesCount(this.Nodes);
            if (scrollInfo.nMax > 0)
            {
                int numOfInvisibleNodes = numOfExpandedNodes - visibleCount;// this.VisibleCount;- (this.ClientRectangle.Height / m_drawnHeight)
                UpdateScrollBar(numOfInvisibleNodes + 2, GetVisibleiNodeIndex(this.TopNode));
            }
            else
            {
                UpdateScrollBar(0, 0);
            }

            this.EndUpdate();
        }

        private int GetVisibleiNodeIndex(TreeNode treeNode)
        {
            int ii = 0;
            foreach (TreeNode node in m_visibleNodesCollection)
            {
                if (node == treeNode)
                {
                    return ii;
                }
                ii++;
            }

            return 0;
        }

        private void UpdateScrollBar(int maximum, int value)
        {
            if (ScrollUpdate != null)
            {
                ScrollUpdate.Invoke(this, new ScrollValuesEventArgs(maximum, value));
            }
        }

        public void Scroll(int value)
        {
            int topNode = value;
            if (topNode >= m_visibleNodesCollection.Count)
            {
                return;
            }

            TreeNode tn = m_visibleNodesCollection[topNode];
            if (tn != null)
            {
                try
                {
                    this.BeginUpdate();
                    this.TopNode = tn;
                    Win32Interop.SendMessage(this.Handle, Win32Interop.WM_SCROLL, (IntPtr)Win32Interop.SB_PAGETOP, IntPtr.Zero);
                }
                finally
                {
                    this.EndUpdate();
                }
            }
        }

        private int GetExpandedNodesCount(TreeNodeCollection treeNodeCollection)
        {
            int expandedNodes = 0;
            foreach (TreeNode node in treeNodeCollection)
            {
                expandedNodes += 1;
                m_visibleNodesCollection.Add(node);
                if (node.IsExpanded == true)
                {
                    expandedNodes += GetExpandedNodesCount(node.Nodes);
                }
            }

            return expandedNodes;
        }

        private int GetVisibleNodesCount(TreeNodeCollection treeNodeCollection, ref int totalHeight)
        {
            int visible = 0;
            foreach (TreeNode node in treeNodeCollection)
            {
                if (totalHeight >= this.ClientRectangle.Height)
                {
                    break;
                }

                visible += 1;

                totalHeight += ((node.Parent == null) ? 30 : 20);

                if (node.IsExpanded == true)
                {
                    visible += GetVisibleNodesCount(node.Nodes, ref totalHeight);
                }
            }

            return visible;
        }

        protected override void OnMouseEnter(EventArgs e)
        {
            //this.Select();
            base.OnMouseEnter(e);
        }

        protected override void OnMouseClick(MouseEventArgs e)
        {
            base.OnMouseClick(e);
            this.Select();
        }

        protected override void OnDrawNode(DrawTreeNodeEventArgs e)
        {
            if (e.Bounds.Y < 0 ||
                e.Bounds.Y > ClientSize.Height)
            {
                return;
            }

            if (m_displayType == MyPhotoIndex.Controls.TreeView.DisplayTypeEnum.Classic)
            {
                OnDrawClassicNode(e);
            }
            else
            {
                OnDrawOutlookNode(e);
            }
        }

        private void OnDrawClassicNode(DrawTreeNodeEventArgs e)
        {
            e.DrawDefault = true;
        }

        protected void OnDrawOutlookNode(DrawTreeNodeEventArgs e)
        {
            e.DrawDefault = false;

            Graphics graphics = e.Graphics;
            Rectangle nodeRect = e.Bounds;

            TreeNode node = e.Node;
            if (nodeRect.Width <= 0 ||
                nodeRect.Height <= 0)
            {
                return;
            }

            bool hasChildNodes = (node.Nodes.Count > 0);
            bool isSelected = m_selectedNodes.Contains(node);// node.IsSelected;

            graphics.Clip.Dispose();
            graphics.Clip = new Region(new RectangleF( new PointF(nodeRect.Location.X - 5, nodeRect.Location.Y), new SizeF(graphics.ClipBounds.Width + 5, graphics.ClipBounds.Height)));
            nodeRect.Width += 5;
            nodeRect.X -=5;

            graphics.InterpolationMode = InterpolationMode.NearestNeighbor;

            if (node.Parent == null)
            {
                if (nodeRect.Height > 0)
                {
                    if (isSelected == false)
                    {
                        using (Brush parentBrush = new LinearGradientBrush(nodeRect,
                            Color.FromArgb(219, 232, 249), Color.FromArgb(158, 177, 207), LinearGradientMode.Vertical))
                        {
                            graphics.FillRectangle(parentBrush, nodeRect);
                        }
                    }
                    else
                    {
                        using (Brush parentBrushSelected = new LinearGradientBrush(nodeRect,
                            Parent.Focused ? Color.FromArgb(195, 255, 195) : Color.FromArgb(212, 208, 200),
                            Parent.Focused ? Color.FromArgb(119, 213, 119) : Color.FromArgb(212, 208, 200),
                            LinearGradientMode.Vertical))
                        {
                            graphics.FillRectangle(parentBrushSelected, nodeRect);
                        }
                    }

                    graphics.DrawLine(Pens.White, new Point(nodeRect.X, nodeRect.Y), new Point(nodeRect.Right, nodeRect.Y));
                    graphics.DrawLine(m_parentBorderPen1, new Point(nodeRect.X, nodeRect.Bottom - 1), new Point(nodeRect.Right, nodeRect.Bottom - 1));
                    graphics.DrawLine(m_parentBorderPen2, new Point(nodeRect.X, nodeRect.Bottom - 2), new Point(nodeRect.Right, nodeRect.Bottom - 2));

                    graphics.DrawLine(Pens.White, new Point(nodeRect.X, nodeRect.Y), new Point(nodeRect.X, nodeRect.Bottom));
                    graphics.DrawLine(Pens.White, new Point(nodeRect.Right - 1, nodeRect.Y), new Point(nodeRect.Right - 1, nodeRect.Bottom));
                }
            }
            else
            {
                if (isSelected == false)
                {
                    graphics.FillRectangle(Brushes.White, nodeRect);
                }
                else
                {
                    using (Brush childBrushSelected = new LinearGradientBrush(nodeRect,
                            Parent.Focused ? Color.FromArgb(195, 255, 195) : Color.FromArgb(212, 208, 200),
                            Parent.Focused ? Color.FromArgb(119, 213, 119) : Color.FromArgb(212, 208, 200),
                            LinearGradientMode.Vertical))
                    {
                        graphics.FillRectangle(childBrushSelected, nodeRect);
                    }
                }

                graphics.DrawLine(m_childBorderPen, new Point(nodeRect.X, nodeRect.Bottom - 1), new Point(nodeRect.Right, nodeRect.Bottom - 1));
            }

            if (this.CheckBoxes == true)
            {
                Image checkBoxImage = node.Checked ? m_checkBoxChecked : m_checkBoxUnChecked;
                Rectangle checkBoxRect = GetCheckBoxRect(node);
                graphics.DrawImageUnscaled(checkBoxImage, checkBoxRect.X, checkBoxRect.Y);

                if (m_filteredNodes.Contains(node) == true)
                {
                    graphics.DrawRectangle(Pens.Red, checkBoxRect);
                }
            }

            ColorMap[] colorMap = new ColorMap[1];
            colorMap[0] = new ColorMap();
            colorMap[0].OldColor = Color.White;
            colorMap[0].NewColor = Color.Transparent;
            ImageAttributes attr = new ImageAttributes();
            attr.SetRemapTable(colorMap);

            int tagImageWidth = 0;

            if (m_icons == true)
            {
                if (string.IsNullOrEmpty(node.ImageKey) == false)
                {
                    Image image = this.ImageList.Images[node.ImageKey];
                    if (image != null)
                    {
                        graphics.DrawImage(image, GetTagRect(node), 0, 0, this.ItemHeight - 2, this.ItemHeight - 2, GraphicsUnit.Pixel, attr);
                        tagImageWidth += image.Width;
                    }
                }
            }

            Rectangle collapseRect = GetCollapseRect(node);

            if (hasChildNodes == true)
            {
                graphics.DrawImage(node.IsExpanded ? m_collapseCollapsedImage : m_collapseClosedImage, collapseRect, 0, 0, 13, 13, GraphicsUnit.Pixel, attr);
            }

            SizeF textSize = graphics.MeasureString(node.Text, m_font);
            Point textPoint = new Point((int)(GetCollapseRect(node).Right + 5 + tagImageWidth), (int)((nodeRect.Y + ((nodeRect.Height - textSize.Height) / 2))));
            graphics.DrawString(node.Text, m_font, m_textBrush, textPoint);
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            DrawNodes(this.Nodes, e.Graphics, e.ClipRectangle);
        }

        private void DrawNodes(TreeNodeCollection treeNodes, Graphics g, Rectangle clipRectangle)
        {
            foreach (TreeNode node in treeNodes)
            {
                Rectangle nodeRect = node.Bounds;
                nodeRect.Width = clipRectangle.Width;
                nodeRect.X = clipRectangle.X;
                OnDrawNode(new DrawTreeNodeEventArgs(g, node, nodeRect, TreeNodeStates.Default));

                if (node.IsExpanded == true)
                {
                    DrawNodes(node.Nodes, g, clipRectangle);
                }
            }
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            Graphics graphics = e.Graphics;

            Color firstColor = (m_displayType == MyPhotoIndex.Controls.TreeView.DisplayTypeEnum.Outlook) ? Color.FromArgb(255, 255, 255) : Color.FromArgb(255, 255, 255);
            graphics.Clear(firstColor);
        }

        private void MyPhotoIndexImageTagTreeView_GiveFeedback(object sender, GiveFeedbackEventArgs e)
        {
            if (e.Effect == DragDropEffects.Link)
            {
                // Show pointer cursor while dragging
                // e.UseDefaultCursors = false;
                // this.Cursor = Cursors.Default;
            }
            else e.UseDefaultCursors = true;
        }

        private void DragDropNode(TreeNode SelectedNode)
        {
            // Reset image list used for drag image
            this.m_imageListDrag.Images.Clear();

            Image tagIcon = null;
            Image tagImage = null;
            IImageTag tag = SelectedNode.Tag as IImageTag;
            if (tag != null &&
                tag.Thumbnail != null)
            {
                tagImage = tag.Thumbnail;
            }

            tagIcon = m_dragDropImage;

            // Create new bitmap
            // This bitmap will contain the tree node image to be dragged
            Bitmap bmp = new Bitmap(tagIcon.Width + 30, tagIcon.Height + 20);

            // Get graphics from bitmap
            Graphics gfx = Graphics.FromImage(bmp);
            gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
            gfx.CompositingQuality = CompositingQuality.HighQuality;

            gfx.DrawImageUnscaled(tagIcon, 0, 0);

            // Draw node label into bitmap
            SizeF stringSize = gfx.MeasureString(SelectedNode.Text, m_font);

            float stringY = tagIcon.Height + 2;
            gfx.DrawString(SelectedNode.Text,
                m_font, Brushes.Black, 0f, stringY);

            // Add bitmap to imagelist
            this.m_imageListDrag.ImageSize = new Size(bmp.Width, bmp.Height);
            this.m_imageListDrag.ColorDepth = ColorDepth.Depth24Bit;
            //   this.m_imageListDrag.TransparentColor = Color.FromArgb(192,192,192);
            this.m_imageListDrag.Images.Add(bmp);

            // Begin dragging image
            if (Win32Interop.ImageDragHelper.ImageList_BeginDrag(this.m_imageListDrag.Handle, 0, tagIcon.Width + 10, 0))
            {
                // Begin dragging
                this.DoDragDrop(SelectedNode, DragDropEffects.Link);
                // End dragging image
                Win32Interop.ImageDragHelper.ImageList_EndDrag();
            }
        }
    }
}
